//
//  AKRequest.m
//  CocoaBean
//
//  Created by sleeper on 6/3/10.
//  Copyright 2010 __MyCompany__ All rights reserved.
//

#import "GDataOAuthAuthentication.h"
#import "NSDictionary_DoubanAddition.h"

#import "AKRequest_Private.h"

#define DEBUG  1

NSString * const kAKRequestParameterKeyStartIndex = @"start-index";
NSString * const kAKRequestParameterKeyMaxResults = @"max-results";

#pragma mark -

@implementation AKRequest

@synthesize delegate = _delegate;
@synthesize identifier = _identifier;
@synthesize tag = _tag;

@synthesize authentication = _auth;

@synthesize url = _url;
@synthesize parameters = _parameters;
@synthesize httpMethod = _httpMethod;
@synthesize appendsJsonParam = _appendsJsonParam;
@synthesize postContent = _postContent;

@synthesize type = _type;

- (NSMutableDictionary *)parameters
{
	if (_parameters == nil) {
		_parameters = [[NSMutableDictionary alloc] init];
	}
	return _parameters;
}

- (void)addIntParameter:(NSUInteger)intParam forKey:(NSString *)key
{
	NSMutableDictionary *params = [self parameters];
	[params setObject:[NSString stringWithFormat:@"%d", intParam] forKey:key];
}

- (NSString *)uuid
{
	if (_uuid == nil) {
		_uuid = AKCreateUUID();
	}
	return _uuid;
}

- (id)init
{
	self = [super init];
	if (self != nil) {
		_appendsJsonParam = YES;
	}
	return self;
}

- (void)dealloc
{
	AK_RELEASE(_auth);
	AK_RELEASE(_parameters);
	AK_RELEASE(_postContent);
	
	AK_RELEASE(_identifier);
	AK_RELEASE(_url);
	AK_RELEASE(_httpMethod);
	AK_RELEASE(_type);	
	AK_RELEASE(_uuid);
	
	AK_RELEASE(_receivedData);
	AK_RELEASE(_connection);
	
	[super dealloc];
}

- (void)setDelegate:(id <AKRequestDelegate>)aDelegate
{
	if (aDelegate != _delegate) {
		_delegate = aDelegate;
		if (_delegate != nil) {
			_requestFlags.delegateRespondsToDidFinish = [_delegate respondsToSelector:@selector(request:didFinishWithData:)];
			_requestFlags.delegateRespondsToDidFail = [_delegate respondsToSelector:@selector(request:didFailWithError:)];
			_requestFlags.delegateRespondsToReceiveOnMainThread = [_delegate respondsToSelector:@selector(calledOnMainThreadForRequest:)];
		}
		else {
			_requestFlags.delegateRespondsToDidFinish = 0;
			_requestFlags.delegateRespondsToDidFail = 0;
			_requestFlags.delegateRespondsToReceiveOnMainThread = 0;
		}
	}
}

- (void)_notifyDelegateWithData:(NSMutableData *)data
{
	[_delegate request:self didFinishWithData:data];
}

- (void)_notifyDelegateWithError:(NSError *)error
{
	[_delegate request:self didFailWithError:error];
}

- (void)main
{
	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
	
	if (![self isCancelled] && self.authentication != nil && self.url != nil) {
		
		GDataOAuthAuthentication *auth = self.authentication;
		NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] initWithURL:self.url] autorelease];
		
		NSString *httpMethod = self.httpMethod;
		if (httpMethod)
			[request setHTTPMethod:httpMethod];
		
		BOOL isPost = [[httpMethod uppercaseString] isEqualToString:@"POST"];
		NSData *postData = nil;
		if (isPost) {
			NSString *postContent = self.postContent;
			if ([postContent length] > 0) {
				postData = [postContent dataUsingEncoding:NSUTF8StringEncoding];
				[request setValue:@"application/atom+xml" forHTTPHeaderField:@"Content-Type"];
				[request setValue:[NSString stringWithFormat:@"%d", [postData length]] forHTTPHeaderField:@"Content-Length"];
				[request setHTTPBody:postData];
			}
		}
		
		NSDictionary *params = nil;
		if (self.appendsJsonParam && !isPost) {
			NSMutableDictionary *tempParams = [NSMutableDictionary dictionaryWithDictionary:self.parameters];
			[tempParams setObject:@"json" forKey:@"alt"];
			params = tempParams;
		}
		else {
			params = self.parameters;
		}
		[auth addExtendedParams:params toRequest:request];
		
		if ([auth authorizeRequest:request]) {
			
			if (isPost && postData != nil) {
			}
			
			_connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
			if (_connection) {
				_receivedData = [[NSMutableData alloc] init];
				
				[_connection start];
				
				// Keep the loop running until we finish loading or are cancelled
				NSRunLoop *theRL = [NSRunLoop currentRunLoop];				
				while (![self isCancelled] && !_requestFlags.connectionFinished) {
					if (![theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:1.0]])
						// This means the connection has removed itself as the input source
						break;
				}
			}
			else {
				NSLog(@"AKRequest: connection cannot be created");
			}
		}
	}
	
	[pool drain];
}

- (void)cancel
{
	[_connection cancel];
	AK_RELEASE(_receivedData);
	[super cancel];
}

#pragma mark Connection Delegate

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    [_receivedData setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [_receivedData appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
#if DEBUG
    NSLog(@"Succeeded! Received %d bytes of data",[_receivedData length]);
#endif
	
	if (![self isCancelled]) {
		BOOL callOnMainThread = YES;
		if (_requestFlags.delegateRespondsToReceiveOnMainThread) {
			callOnMainThread = [_delegate calledOnMainThreadForRequest:self];
		}
		
		if (_requestFlags.delegateRespondsToDidFinish) {
			if (callOnMainThread) {
				[self performSelectorOnMainThread:@selector(_notifyDelegateWithData:) withObject:_receivedData waitUntilDone:YES];
			}
			else {
				[_delegate request:self didFinishWithData:_receivedData];
			}
		}
	}
	
	AK_RELEASE(_connection);
    AK_RELEASE(_receivedData);
	_requestFlags.connectionFinished = 1;
}

- (void)connection:(NSURLConnection *)connection
  didFailWithError:(NSError *)error
{
#if DEBUG
	NSLog(@"request failed with response:\n%@\nerror:%@", [[[NSString alloc] initWithData:_receivedData encoding:NSUTF8StringEncoding] autorelease], error);
#endif
	
	AK_RELEASE(_connection);
    AK_RELEASE(_receivedData);
	
	BOOL callOnMainThread = YES;
	if (_requestFlags.delegateRespondsToReceiveOnMainThread) {
		callOnMainThread = [_delegate calledOnMainThreadForRequest:self];
	}
	
	if (_requestFlags.delegateRespondsToDidFail) {
		if (callOnMainThread) {
			[self performSelectorOnMainThread:@selector(_notifyDelegateWithError:) withObject:error waitUntilDone:YES];
		}
		else {
			[_delegate request:self didFailWithError:error];
		}
	}
	
	_requestFlags.connectionFinished = 1;
}

@end
